﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using frag_boardgame;
using WCFCommon;
using DataBase;
using System.Threading;
using System.Timers;

namespace WCFGameService
{
    public class LobbyService : ILobby
    {

        public static LobbyStaticData lobby = new LobbyStaticData();
        private string userName;
        private static System.Timers.Timer timer = new System.Timers.Timer();
        private const int DETECT_DISCONNECTION_TIME = 3000;
        

        



        private void InitilizeGuest(string name)
        {
            if (lobby.users.Count == 0)
                lobby.NumOfGuests = 1;
            InitilizeLobby(name);
        }
        private void InitilizeUser(string name)
        {   if (lobby.users.Count == 0)
                lobby.NumOfGuests = 0;
            InitilizeLobby(name);

        }
        private void InitilizeLobby(string name)
        {
            CheckClients(false);
            userName = name;
            UserInfo info = new UserInfo();
            info.config = new PlayerConfiguration();
            info.config.Name = name;
            info.lobbycallback = OperationContext.Current.GetCallbackChannel<ILobbyCallback>();

            lobby.users[name] = info;

            if (!lobby.isDisconnectionDetectoron)
            {
                timer.Elapsed += new ElapsedEventHandler(DetectDisconnection);
                timer.Interval = DETECT_DISCONNECTION_TIME;
                timer.Enabled = true;
                lobby.isDisconnectionDetectoron = true;
            }
        }

        private void DetectDisconnection(object source, ElapsedEventArgs e)
        {
            CheckClients(true);
        }
        private void RemoveUser(string user)
        {
            lobby.users.Remove(user);
        }
        //the method gets a name of a user that has disconnented and update all the other users about it.
        private void UpdateClients(string name)
        {
            foreach (UserInfo user in lobby.users.Values)
            {
                user.lobbycallback.Write(name + " disconnected from server");
            }
        }

        private void CheckClients(bool toUpdateClients)
        {
            List<string> toRemove = new List<string>();
            foreach (UserInfo user in lobby.users.Values)
            {
                try
                {
                    user.lobbycallback.CheckClient();
                }
                catch
                {
                    string name = user.config.Name;
                    toRemove.Add(name);



                    foreach (Room room in lobby.rooms.Values)
                    {
                        if (room.Users.ContainsKey(name))
                        {
                            if (room.IsPlaying)
                            {

                                foreach (Player player in room.Game.Data.GetPlayersList())
                                {
                                    if (player.Name == name)
                                    {

                                        player.Control = Player.Controller.AI;
                                    }
                                }

                                //(Still an Error if it is currently the player's turn)
                            }
                            room.Users.Remove(name);

                            room.Info.nPlayers--;

                        }
                    }
                }

            }
            toRemove.ForEach(RemoveUser);
            if (toUpdateClients)
                toRemove.ForEach(UpdateClients);
        }
        #region ILobby Members
        public void Disconnect()
        {
            RemoveUser(userName);

        }



        public RoomInfo[] GetRooms()
        {
            
            return lobby.rooms.Values.Select(delegate(Room x) { return x.Info;  }).ToArray();
        }

        public RoomInfo CreateRoom(string name, GameConfiguration config)
        {
            Board board;
            
            if (lobby.rooms.ContainsKey(name))
            {
                throw new FaultException<RoomCreationFault>(new RoomCreationFault(name, "name '" + name + "' already exists"));
            }

            if (config.BoardName != null)
            {
                DBAdapter dba = new DBAdapter();
                board = dba.getBoard(config.BoardName);
                if (board == null)
                    throw new FaultException<RoomCreationFault>(new RoomCreationFault(name, "board '" + config.BoardName + "' does not exists"));
            }
            else
            {
                board = new Board(config.Rows, config.Cols);
            }


            Room room = new Room();
            room.Info.name = name;
            room.Info.nPlayers = 0;
            room.Info.config = new Configuraion(config);
            room.Info.config.SetBoard(board);
            lobby.rooms.Add(name, room);

            return room.Info;
        }



        public List<string> GetPlayerHistory(string userName)
        {
            List<String> playerHistory;
            try
            {
                playerHistory = new DBAdapter().getPlayerHistory(userName);
            }
            catch (Exception e)
            {
                playerHistory = new List<string>();
            }
            return playerHistory; 

        }

        public List<string> GetPresetBoardsList()
        {
            List<String> boards;
            try
            {
                boards = new DBAdapter().getBoardsList();
            }
            catch (Exception e)
            {
                boards = new List<string>() ;
            }
            return boards;
        }


        public AuthenticationResult Authenticate(string name, string password)
        {
            DBAdapter dba = new DBAdapter();

            if (dba.checkUsername(name)==null)
                return AuthenticationResult.IncorrectUserName;
            if (dba.checkUserPassword(name, password) == null)
                return AuthenticationResult.IncorrectPassword;
            if (lobby.users.ContainsKey(name))
                return AuthenticationResult.UserAlreadyLoggedIn;


            InitilizeUser(name);

            return AuthenticationResult.Succeed;
            

 
        }

        
        public string Guest_Connection()
        {
            
            lobby.NumOfGuests++;
            string name = "Guest" + lobby.NumOfGuests;
            InitilizeGuest(name);
           
            return name;
        }



        public AddingNewUserResult AddUser(string name, string password, string email)
        {
            DBAdapter dba = new DBAdapter();

            if (!DBLibrary.checkEmailValidity(email))
                return AddingNewUserResult.IncorrectEmail;
            if (!dba.isUserNameAvailable(name))
                return AddingNewUserResult.UserExist;
            if (dba.addAccount(name, password, email))
            {
                InitilizeUser(name);
                return  AddingNewUserResult.Succeed;
            }
            else
                return AddingNewUserResult.UnknownError;
        }


        public int GetPlayerScroe(string name)
        {
            DBAdapter db = new DBAdapter();
            return db.getPlayerScore(name);
        }

        #endregion
    }

    
    
    public class LobbyStaticData
    {
        public Dictionary<string, UserInfo> users = new Dictionary<string, UserInfo>();
        public Dictionary<string, Room> rooms = new Dictionary<string, Room>();
        public int NumOfGuests = 0;
        public bool isDisconnectionDetectoron = false;
    }

    public class UserInfo
    {
        public IRoomCallback roomCallback;
        public ILobbyCallback lobbycallback;
        public PlayerConfiguration config;

    }
}
